#pragma once

#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>

#include "Log.hpp"
#include "Common.hpp"
#include "Inet_Addr.hpp"

static const uint16_t g_port = 8888;
static const int g_listensocket = -11;
static const int g_blcklog = 8;

using namespace LogModule;

class TcpServer
{
public:
    TcpServer(uint16_t port = g_port)
        : _port(port), _isrunning(false), _listensocket(g_listensocket)
    {
    }

    void InitServer()
    {
        // 1、创建 监听套接字 socket
        _listensocket = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocket < 0)
        {
            LOG(FATAL, "listensocket create failed\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "listensocket create success, socket is %d\n", _listensocket);
        // 2、创建存储服务端的 IP 和 Port 的结构体
        struct sockaddr_in local;
        bzero(&local, sizeof(local));       // 初始化为0
        local.sin_family = AF_INET;         // 标识网络通信
        local.sin_port = htons(_port);      // 将本地序列转为网络序列
        local.sin_addr.s_addr = INADDR_ANY; // 服务端绑定的 ip 为 0 标识可以接受任何 ip的请求
        // 3、将 listensocket 和 local 绑定
        if (::bind(_listensocket, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            LOG(FATAL, "bind error\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");
        // 4、检测是否成功监听
        if (::listen(_listensocket, g_blcklog) < 0)
        {
            LOG(FATAL, "listen error\n");
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen success\n");
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 创建存储 客户的 ip 和 port 等信息 的结构体
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // 获取 链接
            int sockfd = ::accept(_listensocket, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(FATAL, "accept failed\n");
                continue;
            }
            Inet_Addr addr(client);
            LOG(INFO, "get a new link, client info is %s, socket is: %d\n", addr.AddrStr().c_str(), sockfd);
            // version 1
            Service(sockfd, addr);
        }
    }

    void Service(int sockfd, Inet_Addr addr)
    {
        while (true)
        {
            char buffer[1024];
            // 获取
            ssize_t n = ::read(sockfd, buffer, sizeof(buffer) - 1);
            if(n > 0)
            {
                buffer[n] = 0;
                std::string echo_server = "[echo_server say]# ";
                echo_server += buffer;
                ::write(sockfd, echo_server.c_str(), echo_server.size());
            }
            else if(n == 0)
            {
                LOG(DEBUG, "client %s quit\n", addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(DEBUG, "read error: %s\n", addr.AddrStr().c_str());
                break;
            }
        }
        ::close(sockfd);
    }

    ~TcpServer()
    {
    }

private:
    int _listensocket;
    uint16_t _port;
    bool _isrunning;
};